﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MatrixVsVector
{
	class Program
	{

		// интерфейс вычисления
		public interface IEvaluate
		{
			double Evaluate();
		}

		// класс вектора
		public class Vektor : IEvaluate
		{
			// создадим массив элементов вектора
			private double[] items;

			// размерность вектора
			private int _size;

			// конструктор вектора заданной длины
			public Vektor(int size)
			{
				this._size = size;
				items = new double[size];
			}

			public double Evaluate()
			{
				double result = 0.0;
				for (int i=0; i < _size; i++)
					result += Math.Pow(this[i],2);

				result = Math.Sqrt(result);
				return result;
			}
			// свойство доступа к значению вектора по индексу
			public double this[int i]
			{
				set
				{
					items[i] = value;
				}
				get
				{
					return items[i];
				}
			}

			// метод клонирует вектор вместе с содержимым
			public Vektor Clone()
			{
				Vektor tmp = new Vektor(items.Length);
				for (int i = 0; i < items.Length; i++)
					tmp[i] = items[i];
				return tmp;
			}

			// Метод меняет 2 значения местами
			public void Swap(int i, int j)
			{
				double tmp = items[i];
				items[i] = items[j];
				items[j] = tmp;
			}

			// перегрузка оператора умножения вектора на заданное значение 
			public static Vektor operator *(double k, Vektor v)
			{
				Vektor tmp = v.Clone();
				for (int i = 0; i < tmp._size; i++)
					tmp[i] = k * tmp[i];
				return tmp;
			}

			// перегрузка оператора деления вектора на заданное значение
			public static Vektor operator /(Vektor v, double k)
			{
				Vektor tmp = v.Clone();
				for (int i = 0; i < tmp._size; i++)
					tmp[i] = tmp[i] / k;
				return tmp;
			}

			// перегрузка оператора вычитания двух векторов
			public static Vektor operator -(Vektor v1, Vektor v2)
			{
				Vektor tmp = v1.Clone();
				for (int i = 0; i < v1._size; i++)
					tmp[i] = v1[i] - v2[i];
				return tmp;
			}

			// перегрузка оператора сложения двух векторов
			public static Vektor operator +(Vektor v1, Vektor v2)
			{
				Vektor tmp = v1.Clone();
				for (int i = 0; i < v1._size; i++)
					tmp[i] = v1[i] + v2[i];
				return tmp;
			}

			// метод вывода списка значений вектора через пробел
			public override string ToString()
			{
				string s = "";
				for (int i = 0; i < _size; i++)
					s += items[i].ToString("F") + " ";
				return s.Trim();
			}

		}

		public class Matrix : IEvaluate
		{
			public int nRows;
			public int nColumns;
			private Vektor[] Rows;

			public Matrix(int nDim)
			{
				Rows = new Vektor[nDim];
				nRows = nDim;
				nColumns = nDim;

				for (int i = 0; i < nRows; i++)
					Rows[i] = new Vektor(nColumns);
			}

			// свойство обращения к строке матрицы по индексу
			public Vektor this[int i]
			{
				set
				{
					Rows[i] = value;
				}

				get
				{
					return Rows[i];
				}
			}

			// обращение к элементу матрицы по индексу строки и столбца
			public double this[int i, int j]
			{
				set
				{
					if ((i > nRows) || (j > nColumns))
						throw new Exception("Превышена размерность матрицы");
					Rows[i][j] = value;
				}
				get
				{
					if ((i > nRows) || (j > nColumns))
						throw new Exception("Превышена размерность матрицы");

					return Rows[i][j];
				}
			}

			// возвращает копию матрицы
			public Matrix Clone()
			{
				Matrix tmp = new Matrix(nRows);
				for (int i = 0; i < tmp.nRows; i++)
					tmp[i] = Rows[i].Clone();
				return tmp;
			}

			public double Evaluate()
			{

				double result = 1;

				// частный случай: матрица 1x1
				if (nRows == 1)
				{
					result = this[0][0];
					return result;
				}
				else
					// частный случай: матрица 2x2
					if (nRows == 2)
					{
						result = this[0][0] * this[1][1] - this[0][1] * this[1][0];
						return result;
					}
					else // Вычисляем по методу Гаусса
					{
						Matrix DupMatrix = this.Clone();
						// матрицу приводим к треугольному виду
						for (int k = 1; k < nRows; k++)
						{
							for (int i = k; i < nRows; i++)
							{
								double res = DupMatrix[i, k - 1] / DupMatrix[(k - 1), (k - 1)];
								for (int j = 0; j < nRows; j++)
								{
									DupMatrix[i, j] -= res * DupMatrix[(k - 1), j];
								}
							}
						}

						// перемножим элементы на главной диагонали
						for (int i = 0; i < nRows; i++)
						{
							result *= DupMatrix[i, i];
						}
					}
					return result;
			}

			public override string ToString()
			{
				string s = "";
				for (int i = 0; i < nRows; i++)
					s += Rows[i].ToString() + " \r\n";
				return s.Trim();
			}

		}

		static void Main(string[] args)
		{
			// создаём матрицу 3x3
			Matrix matr = new Matrix(3);
			
			Vektor v = matr[0];
			v[0] = 1;
			v[1] = 2;
			v[2] = 3;
			Console.WriteLine("Длина вектора v[0]: " + v.Evaluate());
			v = matr[1];
			v[0] = 4;
			v[1] = 7;
			v[2] = 6;
			Console.WriteLine("Длина вектора v[1]: " + v.Evaluate());
			v = matr[2];
			v[0] = 7;
			v[1] = 8;
			v[2] = 9;
			Console.WriteLine("Длина вектора v[2]: " + v.Evaluate());
			Console.WriteLine();
			Console.WriteLine("Исходная матрица:");

			Console.WriteLine(matr.ToString());
			Console.WriteLine();
			Console.WriteLine("Определитель матрицы = " + matr.Evaluate());
			Console.ReadKey(true);
		}
	}
}
